/****************************************************************************\
 * @file     BR_Misc.c
 * @author   傅帅
 * @date     2019/12/25
 * @brief    杂毛
 *
 ******************************************************************************
 * @note    本文件存放操作系统及模板的一些初始化函数;
            整合的正点原子usart.c delay.c stmflash.c中的部分内容;
            及实现一些通用功能函数;
 ****************************************************************************/
#include "stdio.h"
#include "stdarg.h"
#include "string.h"
#include "stm32f4xx.h"
#include "FreeRTOS.h"
#include "task.h"
#include "BR_Common.h"

/** @brief  一些在开启任务调度前进行的初始化 **/
void BR_vTemplateInit(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 设置系统中断优先级分组4
    BR_vSysTickInit(168);	                        // 初始化SysTick
    #if BR_configUSE_CONSOLE == 1
        BR_vConsoleInit();
    #endif
    BR_vRemoteControllerInit();
    BR_vLocatorInit();
    BR_vCenterControllerInit();
    BR_vErrorManagerInit();
    BR_vServoMotorInit();
    BR_vDrivingMotorInit();
}

/** @brief  一些需要开启任务调度后进行的初始化 **/
void BR_vTemplateTaskInit(void)
{
    #if BR_configUSE_STAT == 1
        BR_vStatInit();
    #endif
    BR_vServoMotorInitSend( 1.5,  0,        4,  0,      10000,  // 速度环PID
                            0.5,  0.001,    2,  1000,   55000); // 位置环PID
}

/** @brief  模板内所有任务创建 **/
void BR_vTemplateTaskCreate(void)
{
    #if BR_configUSE_STAT == 1
        xTaskCreate(                                            // 统计任务创建 2
            (TaskFunction_t) BR_vStatTask,
            (char *)         "BR_StatTask",
            (uint16_t)       BR_StatTaskStkSize,
            (void * )        NULL,
            (UBaseType_t)    BR_StatTaskPrio,
            (TaskHandle_t*)  &BR_StatTaskHandler);
    #endif
    #if BR_configUSE_CONSOLE == 1
        xTaskCreate(                                            // 控制台消息处理任务创建 3
            (TaskFunction_t) BR_vConsoleHandlerTask,
            (char *)         "BR_ConsoleHandlerTask",
            (uint16_t)       BR_ConsoleHandlerTaskStkSize,
            (void * )        NULL,
            (UBaseType_t)    BR_ConsoleHandlerTaskPrio,
            (TaskHandle_t*)  &BR_ConsoleHandlerTaskHandler);
        xTaskCreate(                                            // 控制台消息发送任务创建 4
            (TaskFunction_t) BR_vConsolePosterTask,
            (char *)         "BR_ConsolePosterTask",
            (uint16_t)       BR_ConsolePosterTaskStkSize,
            (void * )        NULL,
            (UBaseType_t)    BR_ConsolePosterTaskPrio,
            (TaskHandle_t*)  &BR_ConsolePosterTaskHandler);
    #endif  
    xTaskCreate(                                                // 遥控器消息处理任务创建 5
        (TaskFunction_t) BR_vRemoteControllerHandlerTask,
        (char *)         "BR_RemoteControllerHandlerTask",
        (uint16_t)       BR_RemoteControllerHandlerTaskStkSize,
        (void * )        NULL,
        (UBaseType_t)    BR_RemoteControllerHandlerTaskPrio,
        (TaskHandle_t*)  &BR_RemoteControllerHandlerTaskHandler);
    xTaskCreate(                                                // 遥控器消息发送任务创建 6
        (TaskFunction_t) BR_vRemoteControllerPosterTask,
        (char *)         "BR_RemoteControllerPosterTask",
        (uint16_t)       BR_RemoteControllerPosterTaskStkSize,
        (void * )        NULL,
        (UBaseType_t)    BR_RemoteControllerPosterTaskPrio,
        (TaskHandle_t*)  &BR_RemoteControllerPosterTaskHandler);
    xTaskCreate(                                                // 全场定位消息处理任务创建 7
        (TaskFunction_t) BR_vLocatorHandlerTask,
        (char *)         "BR_LocatorHandlerTask",
        (uint16_t)       BR_LocatorHandlerTaskStkSize,
        (void * )        NULL,
        (UBaseType_t)    BR_LocatorHandlerTaskPrio,
        (TaskHandle_t*)  &BR_LocatorHandlerTaskHandler);
    xTaskCreate(                                                // 驱动电机消息处理任务创建 13
        (TaskFunction_t) BR_vDrivingMotorHandlerTask,
        (char *)         "BR_DrivingMotorHandlerTask",
        (uint16_t)       BR_DrivingMotorHandlerTaskStkSize,
        (void * )        NULL,
        (UBaseType_t)    BR_DrivingMotorHandlerTaskPrio,
        (TaskHandle_t*)  &BR_DrivingMotorHandlerTaskHandler);
    xTaskCreate(                                                // 舵向电机消息处理任务创建 14
        (TaskFunction_t) BR_vServoMotorHandlerTask,
        (char *)         "BR_ServoMotorHandlerTask",
        (uint16_t)       BR_ServoMotorHandlerTaskStkSize,
        (void * )        NULL,
        (UBaseType_t)    BR_ServoMotorHandlerTaskPrio,
        (TaskHandle_t*)  &BR_ServoMotorHandlerTaskHandler);
    xTaskCreate(                                                // 主控任务创建 15
        (TaskFunction_t) BR_vCenterControllerTask,
        (char *)         "BR_CenterControllerTask",
        (uint16_t)       BR_CenterControllerTaskStkSize,
        (void * )        NULL,
        (UBaseType_t)    BR_CenterControllerTaskPrio,
        (TaskHandle_t*)  &BR_CenterControllerTaskHandler);
    xTaskCreate(                                                // 错误管理任务创建 16
        (TaskFunction_t) BR_vErrorManagerTask,
        (char *)         "BR_ErrorManagerTask",
        (uint16_t)       BR_ErrorManagerTaskStkSize,
        (void * )        NULL,
        (UBaseType_t)    BR_ErrorManagerTaskPrio,
        (TaskHandle_t*)  &BR_ErrorManagerTaskHandler);
    
}

/** @brief  机构相关任务创建 **/
void BR_vMechanismTaskCreate(void)
{   
}

/**
 * @brief  MemCatenate 解析数据格式，进行内存拼接(C标准库为啥没这玩意 -_-!)
 * @param  TargetAddr  : 目标区块起始地址
            Size        : 目标区块大小
            Format      : 格式化字符串
            ...         : 不定长参数
 * @retval 拼接后的总长度(Byte)
 * @note   处理字符串时本函数不会在末尾加0x00
 */
uint8_t BR_cMemCat(uint8_t *pTargetAddr, uint8_t Size, const char *Format, ...)
{
    va_list pA;
    uint8_t *pTmp = pTargetAddr;
    va_start(pA, Format);
    memset(pTargetAddr, 0, Size);   // 清空缓冲区
    while(*Format != '\0')          // 遍历格式化字符串
    {
        if(*Format != '%')              // 非格式化情况
        {
            memcpy(pTmp++, Format, 1);
        }else                           // 格式化情况
        {
            switch(*++Format)               // 判断是何种情况
            {
                case 'c':           // 8字节
                {
                    char tmp = (char)va_arg(pA, int);
                    memcpy(pTmp++, &tmp, 1);
                    break;
                }
                case 'h':           // 16字节 short
                {
                    short tmp = (short)va_arg(pA, int);
                    memcpy(pTmp, &tmp, 2);
                    pTmp += 2;
                    break;
                }
                case 'f':           // 32字节 float
                {
                    float tmp = (float)va_arg(pA, double);
                    memcpy(pTmp, &tmp, 4);
                    pTmp += 4;
                    break;
                }
                case 'd':           // 32字节 int
                {
                    int tmp = (int)va_arg(pA, int);
                    memcpy(pTmp, &tmp, 4);
                    pTmp += 4;
                    break;
                }
                case 's':           // 字符串
                {
                    char *p_tmp = va_arg(pA, char*);
                    uint8_t tmp_size = strlen(p_tmp);
                    memcpy(pTmp, p_tmp, tmp_size);
                    pTmp += tmp_size;
                    break;
                }
            }
        }
        Format++;  // 继续遍历
    }
    return pTmp - pTargetAddr;
}

/** @brief 输入需要计算校验位的字符串，模256校验计算，并将校验结果加至末尾 **/
void BR_vCheckByteCalculateAppend(uint8_t *pStr, uint8_t Len)
{
    uint8_t Sum = 0;
    for(uint8_t i = 0; i < Len; i++) Sum += *pStr++;
    *pStr = (uint8_t)Sum;
}

/** @brief 输入需要计算校验位的字符串，CRC校验计算，并将校验结果加至末尾 CRC初始位0xAA 多项式0x7D**/
void BR_vCRCCheckByteCalculateAppend(uint8_t *pStr, uint8_t Len)
{
    uint8_t crc = 0xAA, polynomial=0x7D;
    for(uint8_t i = 0; i < Len; i++)
    {
        crc = crc ^ (*pStr++);
        for (uint8_t j = 0; j < 8; i++)
        {
            if (crc & 0x01)
            {
                crc = (crc >> 1) ^ polynomial;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    *pStr = crc;
}

/* ************************** SysTick相关 ************************* */
extern void xPortSysTickHandler(void);
static u8 SysTickCountsPerUs;

/** @brief  设定SysTick时钟源和重装载值 **/
void BR_vSysTickInit(uint8_t Sysclk)
{
    uint32_t Reload;
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);      // 设置SysTick时钟源为不分频HCLK
    Reload = Sysclk * 1000000 / configTICK_RATE_HZ;
    SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
    SysTick->LOAD = Reload;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    SysTickCountsPerUs = Sysclk;  // 每us SysTick计数器减掉的counts 使用操作系统时，SysTick时钟源不分频，每us计数即是168000000/1000000
}

/** @brief  不用系统延时的us延时 **/
void delay_us(u32 nus)
{
    u32 ticks;
    u32 told,tnow,tcnt = 0;
    u32 reload = SysTick->LOAD;                         // LOAD的值
    ticks = nus * SysTickCountsPerUs;                   // 需要的节拍数
    taskENTER_CRITICAL();
    told = SysTick->VAL;                                // 刚进入时的计数器值
    while(1)
    {
        tnow = SysTick->VAL;
        if(tnow != told)
        {
            if(tnow < told) tcnt += told - tnow;        // 这里注意一下SYSTICK是一个递减的计数器就可以了.
            else tcnt += reload - tnow + told;
            told = tnow;
            if(tcnt >= ticks) break;                    // 时间超过/等于要延迟的时间,则退出.
        }
    };
    taskEXIT_CRITICAL();
}

/** @brief  不用系统延时的ms延时 **/
void delay_ms(u16 nms)
{
    delay_us((u32)( nms * 1000 ));                              // 普通方式延时
}

void SysTick_Handler(void)
{
    if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)   // 系统已经运行
    {
        xPortSysTickHandler();
    }
}

/* ************************** usart.c中部分 ************************* */
//标准库需要的支持函数
struct __FILE
{
    int handle;
};

FILE __stdout;

//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
    x = x;
}

//__use_no_semihosting was requested, but _ttywrch was
void _ttywrch(int ch)
{
    ch = ch;
}

//重定义fputc函数
int fputc(int ch, FILE *f)
{
    while(( USART2->SR & 0X40 ) == 0); //循环发送,直到发送完毕
    USART2->DR = (u8) ch;
    return ch;
}

/* ************************** Flash读写 ************************* */
//读取指定地址的半字(16位数据)
//faddr:读地址
//返回值:对应数据.
u32 STMFLASH_ReadWord(u32 faddr)
{
    return *(vu32*)faddr;
}

//获取某个地址所在的flash扇区
//addr:flash地址
//返回值:0~11,即addr所在的扇区
uint16_t STMFLASH_GetFlashSector(u32 addr)
{
    if(addr < ADDR_FLASH_SECTOR_1) return FLASH_Sector_0;
    else if(addr < ADDR_FLASH_SECTOR_2) return FLASH_Sector_1;
    else if(addr < ADDR_FLASH_SECTOR_3) return FLASH_Sector_2;
    else if(addr < ADDR_FLASH_SECTOR_4) return FLASH_Sector_3;
    else if(addr < ADDR_FLASH_SECTOR_5) return FLASH_Sector_4;
    else if(addr < ADDR_FLASH_SECTOR_6) return FLASH_Sector_5;
    else if(addr < ADDR_FLASH_SECTOR_7) return FLASH_Sector_6;
    else if(addr < ADDR_FLASH_SECTOR_8) return FLASH_Sector_7;
    else if(addr < ADDR_FLASH_SECTOR_9) return FLASH_Sector_8;
    else if(addr < ADDR_FLASH_SECTOR_10) return FLASH_Sector_9;
    else if(addr < ADDR_FLASH_SECTOR_11) return FLASH_Sector_10;
    return FLASH_Sector_11;
}

//从指定地址开始写入指定长度的数据
//特别注意:因为STM32F4的扇区实在太大,没办法本地保存扇区数据,所以本函数
//         写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
//         写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
//         没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
//该函数对OTP区域也有效!可以用来写OTP区!
//OTP区域地址范围:0X1FFF7800~0X1FFF7A0F
//WriteAddr:起始地址(此地址必须为4的倍数!!)
//pBuffer:数据指针
//NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)
{
    FLASH_Status status = FLASH_COMPLETE;
    u32 addrx = 0;
    u32 endaddr = 0;
    if(WriteAddr < STM32_FLASH_BASE || WriteAddr % 4) return; //非法地址
    FLASH_Unlock();                                                                     //解锁
    FLASH_DataCacheCmd(DISABLE);//FLASH擦除期间,必须禁止数据缓存

    addrx = WriteAddr;                                  //写入的起始地址
    endaddr = WriteAddr + NumToWrite * 4; //写入的结束地址
    if(addrx < 0X1FFF0000)                      //只有主存储区,才需要执行擦除操作!!
    {
        while(addrx < endaddr)                  //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
        {
            if(STMFLASH_ReadWord(addrx) != 0XFFFFFFFF)          //有非0XFFFFFFFF的地方,要擦除这个扇区
            {
                status = FLASH_EraseSector(STMFLASH_GetFlashSector(addrx),VoltageRange_3);              //VCC=2.7~3.6V之间!!
                if(status != FLASH_COMPLETE) break;                     //发生错误了
            }else addrx += 4;
        }
    }
    if(status == FLASH_COMPLETE)
    {
        while(WriteAddr < endaddr)      //写数据
        {
            if(FLASH_ProgramWord(WriteAddr,*pBuffer) != FLASH_COMPLETE)          //写入数据
            {
                break;                  //写入异常
            }
            WriteAddr += 4;
            pBuffer++;
        }
    }
    FLASH_DataCacheCmd(ENABLE); //FLASH擦除结束,开启数据缓存
    FLASH_Lock();    //上锁
}

//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToRead:字(4位)数
void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)
{
    u32 i;
    for(i = 0; i < NumToRead; i++)
    {
        pBuffer[i] = STMFLASH_ReadWord(ReadAddr);      //读取4个字节.
        ReadAddr += 4;      //偏移4个字节.
    }
}
